#!/usr/bin/env python
# -*- coding: utf-8 -*-
import testlink
import openpyxl as op
from openpyxl.styles import Alignment
import time
import re
from enum import Enum
import json
import sys

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import PyQt5.QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, qApp, QWidget, QFileDialog, QDialog
from PyQt5.QtCore import Qt, QEvent, QObject
from functools import partial
import os
import time
from PyQt5.QtCore import QObject, pyqtSignal, QEventLoop, QTimer, QThread, QTime
from PyQt5.QtWidgets import QTextEdit

class TESTLINK_CONFIG(Enum):
    testlink_server_url = "http://192.168.0.201/testlink/lib/api/xmlrpc/v1/xmlrpc.php"
    testlink_server_key = "a695a58b5cde364a3a669bac4302054a"
    test_plan_testlink = 'all'  # 'uart1'#'uart0'#'UART'#'all' #'SPI'
    build_version_in_plan_testlink = 'v1.1'

class Ui_MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(MainWindow)
        self.tlc = testlink.TestlinkAPIClient("http://192.168.0.201/testlink/lib/api/xmlrpc/v1/xmlrpc.php", "a695a58b5cde364a3a669bac4302054a")
        self.th = MyThread()
        self.th.signalForText.connect(self.outputWritten)               #线程的信号内容绑定到textBrowser
        sys.stdout = self.th  

    def outputWritten(self, text):                                      #输出到textBrowser
        cursor = self.textBrowser.textCursor()                          #获取指针对象
        # cursor.movePosition(QtGui.QTextCursor.End)                    #鼠标光标位置
        cursor.insertText(text)                                         #将外部数据放入指针内
        self.textBrowser.setTextCursor(cursor)                          #指针对象指向testbrowser
        # self.textBrowser.ensureCursorVisible()

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(539, 262)
        MainWindow.setMaximumSize(QtCore.QSize(539, 290))
        font = QtGui.QFont()
        font.setFamily("微软雅黑")
        font.setPointSize(12)
        MainWindow.setFont(font)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(340, 190, 161, 41))
        self.pushButton.setObjectName("pushButton")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(20, 20, 71, 31))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(30, 80, 51, 31))
        self.label_2.setObjectName("label_2")
        self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
        self.textBrowser.setGeometry(QtCore.QRect(250, 0, 281, 181))
        self.textBrowser.setObjectName("textBrowser")
        self.comboBox = QtWidgets.QComboBox(self.centralwidget)
        self.comboBox.setGeometry(QtCore.QRect(100, 20, 141, 31))
        self.comboBox.setObjectName("comboBox")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(100, 80, 141, 31))
        self.lineEdit.setObjectName("lineEdit")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(10, 130, 91, 51))
        self.label_4.setObjectName("label_4")
        self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_2.setGeometry(QtCore.QRect(100, 140, 141, 31))
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(0, 200, 111, 31))
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "自动上传excel"))
        self.pushButton.setText(_translate("MainWindow", "开始上传至testlink"))
        self.label.setText(_translate("MainWindow", "工程版本"))
        self.label_2.setText(_translate("MainWindow", "文件名"))
        self.label_4.setText(_translate("MainWindow", "一级目录名"))
        self.pushButton_2.setText(_translate("MainWindow", "其它注意事项"))

        self.textBrowser.append("温馨提示:\n1、上传文件需和该exe同一目录\n2、请输入正确的文件名（区分大小写），如：UART\n3、请确认选择的工程版本没有问题\n\n最后：若上述都确认无误后再启动。\n")
        self.pushButton.clicked.connect(self.start_gogogo)
        self.pushButton_2.clicked.connect(self.showDialog)

    def showDialog(self):
        Dialog = Ui_Dialog()
        Dialog.setupUi(Dialog)
        Dialog.show()
        Dialog.exec_()
        
    def get_textcase(self):
        self.comboBox.clear()
        test_url= "http://192.168.0.201/testlink/lib/api/xmlrpc/v1/xmlrpc.php"
        test_key = "a695a58b5cde364a3a669bac4302054a"
        do_link = TestLink(test_url, test_key)
        list_1 = do_link.available_projects
        for i in list_1:
            # print(i['name'])
            self.comboBox.addItems(i['name'].split(','))

    def start_gogogo(self):
        self.textBrowser.clear()
        self.th.start()


class Ui_Dialog(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()

    def setupUi(self,Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(617, 395)
        self.setWindowFlags(Qt.WindowCloseButtonHint)
        self.setWindowTitle('注意事项')

        self.label = QtWidgets.QLabel(Dialog)
        self.label_2 = QtWidgets.QLabel(Dialog)
        self.label_3 = QtWidgets.QLabel(Dialog)
        self.label_4 = QtWidgets.QLabel(Dialog)
        self.label_5 = QtWidgets.QLabel(Dialog)
        self.label_6 = QtWidgets.QLabel(Dialog)
        self.label_7 = QtWidgets.QLabel(Dialog)
        self.label_8 = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(30, 40, 2331, 41))
        self.label_2.setGeometry(QtCore.QRect(30, 80, 2331, 41))
        self.label_3.setGeometry(QtCore.QRect(30, 120, 2331, 41))
        self.label_4.setGeometry(QtCore.QRect(30, 160, 2331, 41))
        self.label_5.setGeometry(QtCore.QRect(30, 200, 2331, 41))
        self.label_6.setGeometry(QtCore.QRect(30, 240, 2331, 41))
        self.label_7.setGeometry(QtCore.QRect(10, 10, 2331, 41))
        self.label_8.setGeometry(QtCore.QRect(10, 320, 2331, 41))
        font = QtGui.QFont()
        font.setPointSize(11)
        self.label.setFont(font)
        self.label.setObjectName("label")
        self.label_2.setFont(font)
        self.label_2.setObjectName("label_2")
        self.label_3.setFont(font)
        self.label_3.setObjectName("label_3")
        self.label_4.setFont(font)
        self.label_4.setObjectName("label_4")
        self.label_5.setFont(font)
        self.label_5.setObjectName("label_5")
        self.label_6.setFont(font)
        self.label_6.setObjectName("label_6")
        self.label_7.setFont(font)
        self.label_7.setObjectName("label_7")
        self.label_8.setFont(font)
        self.label_8.setObjectName("label_8")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, QDialog):
        _translate = QtCore.QCoreApplication.translate
        self.label.setText(_translate("Dialog", "1、在excel上建立新的sheet需与testlink上新目录名保持一致。"))
        self.label_2.setText(_translate("Dialog", "2、目前excel格式固定，不支持修改列属性（否则需修改脚本配合）"))
        self.label_3.setText(_translate("Dialog", "3、excel中纯数字格式需修改为文本格式的数字，否则会报错"))
        self.label_4.setText(_translate("Dialog", "4、前提、摘要建议直接用testlink下载的格式（xml文本不作处理），这样\n"
"可以支持图片等的信息保留"))
        self.label_5.setText(_translate("Dialog", "5、testlink_creat.py脚本会将选定的sheet的所有case传至对应的suite中,\n"
"所以只是新增case时，应把excel中原有的case删除，防止创建重复case"))
        self.label_6.setText(_translate("Dialog", "6、Testlink上目录需提前写好，比如UART的所有二级目录。"))
        self.label_7.setText(_translate("Dialog", "*—————————————————————————————————————*"))
        self.label_8.setText(_translate("Dialog", "*—————————————————————————————————————*"))

class TESTLINK_CASE_TEST(Enum):
    AUTO_TEST = '2'
    MANUAL_TEST = '1'

class TESTLINK_CASE_ACTIVE(Enum):
    ACTIVE = '1'
    INACTIVE = '0'

def data_from_testlink(data):
    """
    用于处理从testlink中获取的数据进行处理
    :param data: 传入的数据
    :return: 返回数据为处理后的数据
    """
    data = data.replace('\r', '')
    data = data.replace('\t', '')
    data = data.replace('\n', '')
    data = data.replace('&nbsp;', ' ')  # 替换空格
    data = data.replace('&rsquo;', '"')  # 转换中文单引号 ’为英文的 "
    data = data.replace('&lsquo;', '"')  # 转换中文单引号‘ 为英文的 "
    data = data.replace('&ldquo;', '"')  # 转换中文双引号“ 为英文的 "
    data = data.replace('&rdquo;', '"')  # 转换中文双引号 ” 为英文的 "
    data = data.replace('：', ":")  # 转换中文的冒号 ：为英文的冒号 :
    data = data.replace('，', ',')  # 转换中文的逗号 ，为英文的逗号 ,
    data = data.replace('&quot;', '\"')  # 转换 &quot; 为双引号
    data = data.replace('&#39;', '\'')  # 转换 &#39;  为单引号
    data = data.replace('｛', '{')  # 转换中文｛ 为英文的 {
    data = data.replace('｝', '}')  # 转换中文 ｝ 为英文的 }
    data = data.replace('&lt;', '<')  # 转换 &lt; 为 <
    data = data.replace('&gt;', '>')  # 转换 &gt为 >
    data = data.replace('&amp;', '&')
    data = data.replace('【', '[')
    data = data.replace('】', ']')
    # 查找类似这类内容 </pre>、</div> 并替换为空
    result_list = re.findall('</.?>', data)
    if result_list:
        for item in result_list:
            data = data.replace(item, '')
    # 查找类似这类内容 <pre style="background-color:#ffffff;color:#000000;font-family:'宋体';font-size:12pt;">  并替换为空
    result_list = re.findall('<.?[^<]*>', data)
    if result_list:
        for item in result_list:
            data = data.replace(item, '')

    data = data.lstrip()

    return data

class MyThread(QThread):
    signalForText = pyqtSignal(str)                                     #信号数据绑定pyqtSignal

    def write(self, text):                                              #发送文本
        self.signalForText.emit(str(text)) 

    def __init__(self, Data = None ,parent=None):                       #继承QT线程函数
        super(MyThread, self).__init__(parent)

    def file_name(self):
        a = ui.lineEdit.text() + '.xlsx'
        return a

    def run(self):
        # print(self.file_name())
        print(time.asctime(time.localtime((time.time()))))
    def run(self):
        # print(self.file_name())
        print(time.asctime(time.localtime((time.time()))))
        try:
            test_url = TESTLINK_CONFIG.testlink_server_url.value
            test_key = TESTLINK_CONFIG.testlink_server_key.value
            test_project = ui.comboBox.currentText()
            do_link = TestLink(test_url, test_key)
            # do_link.query_available_projects()
            print(do_link.set_cur_project(test_project))
            do_link.query_first_level_testsuits_for_project()
            do_link.set_current_suite(ui.lineEdit_2.text())
            print(do_link.current_suite_info)
            do_link.query_second_level_testsuits_for_project(do_link.current_suite_info)

            wb = op.load_workbook(self.file_name())           #文件目录为最外面

            cases = get_case_info_excel(wb,do_link)
            print(cases)
            for case_info in cases:
                do_link.creat_case_for_suite(case_info)
            print('---------------------------------')
            print('-------------finish!-------------')
            print('---------------------------------')
            print('---------------------------------')
            print('-------------finish!-------------')
            print('---------------------------------')
        except:
            print(Exception)


class TestLink:
    def __init__(self, url, key):
        self.current_project = None
        self.current_plan = None
        self.current_build = None
        self.current_plan_name = None
        self.current_suite = []
        self.current_case = []
        self.first_level_suites = []
        self.second_level_suites = []
        self.current_suite_info= {}

        self.available_projects = []
        self.available_suite = None
        self.available_plan = None

        self.tlc = testlink.TestlinkAPIClient(url, key)
        projects = self.tlc.getProjects()
        for prj in projects:
            if prj['active'] == '1':
                project = {}
                project['name'] = prj['name']
                project['id'] = prj['id']
                self.available_projects.append(project);
        # print(self.available_projects)

    def query_available_projects(self):
        print(self.available_projects)
        return self.available_projects

    def set_cur_project(self, project_name):
        self.current_project = None
        self.available_plan = []
        self.available_suite = []

        find_project = False
        for prj in self.available_projects:
            if prj['name'] == project_name:
                prj_id = prj['id']
                find_project = True
                break;

        if find_project:
            self.current_project = prj_id
            project_plan = self.tlc.getProjectTestPlans(prj_id)
            for plan in project_plan:
                if plan['active'] == '1':
                    pj_plan = {}
                    pj_plan['name'] = plan['name']
                    pj_plan['id'] = plan['id']
                    self.available_plan.append(pj_plan);

            project_suite = self.tlc.getFirstLevelTestSuitesForTestProject(prj_id)
            # print(project_suite)
            for suite in project_suite:
                pj_suite = {}
                pj_suite['name'] = suite['name']
                pj_suite['id'] = suite['id']
                self.available_suite.append(pj_suite);

        return self.current_project

    def query_available_plan(self):
        print(self.available_plan)
        return self.available_plan

    def set_cur_plan(self, plan_name, build_name=None):
        # print('set_cur_plan')
        self.current_case = []
        find_plan = False

        # plan_id = self.tlc.getTestPlanByName(self.current_project, plan_name)[0]['id']
        for plan in self.available_plan:
            if plan['name'] == plan_name:
                plan_id = plan['id']
                find_plan = True
                #------spi�����Զ��޸�-hlm---------
                self.current_plan_name = plan_name
                #------��--------------------------
                break;

        if find_plan:
            self.current_plan = plan_id
            build_in_plan = self.tlc.getBuildsForTestPlan(testplanid=plan_id)
            find_build = False
            print(build_in_plan)
            for build in build_in_plan:
                if build_name == None:
                    self.current_build = build['id']
                    find_build = True

                if build['name'] == build_name:
                    self.current_build = build['id']
                    find_build = True
                    break;
            print(self.current_build)
            if find_build:
                plan_suite = self.tlc.getTestSuitesForTestPlan(testplanid=plan_id, buildid=self.current_build)
                # print(plan_suite)

                for suite in plan_suite:
                    if suite['parent_id'] == self.current_project:
                        pl_suite = {}
                        pl_suite['name'] = suite['name']
                        pl_suite['id'] = suite['id']
                        pl_suite['case_id'] = self.get_testcases_via_suite(suite['id'])
                        self.current_suite.append(pl_suite)

                plan_case = self.tlc.getTestCasesForTestPlan(testplanid=plan_id, buildid=self.current_build)
                # print(plan_case)
                # print(self.current_suite)
                for caseid, case_descrip in plan_case.items():
                    if case_descrip[0]['execution_type'] == TESTLINK_CASE_TEST.AUTO_TEST.value:
                        pl_case = {}
                        pl_case['id'] = caseid
                        pl_case['suitename'] = self.get_suitename_via_caseid(caseid, self.current_suite)
                        self.current_case.append(pl_case)
                # print(self.current_case)
        return self.current_plan

    def filter_obj_IPs(self, obj_IPs):
        filter_case = []
        if len(self.current_case) == 0:
            return None
        if len(self.obj_IPs) == 0:
            return self.current_case

        for ip in obj_IPs:
            for tcase in self.current_case:
                if ip == tcase['suitename']:
                    filter_case.append(tcase)
        self.current_case = filter_case

        return self.current_case

    def get_testcases_via_suite(self, suite_id):
        testcaseid = []
        suite_case = self.tlc.getTestCasesForTestSuite(testsuiteid=suite_id, deep=True)
        for case in suite_case:
            testcaseid.append(case['id'])
        return testcaseid

    def get_suitename_via_caseid(self, case_id, suite_case_lib):
        for suite in suite_case_lib:
            suite_case = suite['case_id']
            for case in suite_case:
                if case == case_id:
                    return suite['name']
        return None

    def query_plan_testcase_lib(self, plan_name=None):
        if plan_name == None:
            pass
        else:
            self.set_cur_plan(plan_name)

        print(self.current_case)

        return self.current_case

    def query_plan_testcase_sum(self, plan_name=None):
        if plan_name == None:
            pass
        else:
            self.set_cur_plan(plan_name)
        return len(self.current_case)

    def query_plan_testcase(self, index, plan_name=None):
        if plan_name == None:
            pass
        else:
            self.set_cur_plan(plan_name)

        if index >= len(self.current_case):
            return None, None
        suite_name = self.current_case[index]['suitename']
        id = self.current_case[index]['id']
        case = self.tlc.getTestCase(id)
        return suite_name, id, case[0]['full_tc_external_id']

    def send_test_result(self, case_id, test_result):
        if test_result == True:
            self.tlc.reportTCResult(case_id, self.current_plan, None, 'p', "", guess=True, platformname="",
                                    buildid=self.current_build)
        else:
            self.tlc.reportTCResult(case_id, self.current_plan, None, 'f', "", guess=True, platformname="",
                                    buildid=self.current_build)

    def query_testcase(self, caseid):
        return self.tlc.getTestCase(caseid)

    def query_first_level_testsuits_for_project(self):
        self.first_level_suites = []
        fl_suites = self.tlc.getFirstLevelTestSuitesForTestProject(self.current_project)
        for suite in fl_suites:
            cur_suite={}
            cur_suite['name']=suite['name']
            cur_suite['id']=suite['id']
            self.first_level_suites.append(cur_suite)
        return self.first_level_suites

    def set_current_suite(self,suite_name):
        find= False
        for cur_suite in self.first_level_suites:
            if cur_suite['name'] == suite_name:
                self.current_suite_info['name'] = suite_name
                self.current_suite_info['id'] = cur_suite['id']
                find =True
        if find:
            return self.current_suite_info
        else:
            print('suite cannot find')
            return self.current_suite_info


    def query_second_level_testsuits_for_project(self,fl_suites):
        self.second_level_suites = []
        sl_suites = self.tlc.getTestSuitesForTestSuite(fl_suites['id'])
        if sl_suites == []:    #若没有二级目录，继承一级目录
            self.second_level_suites.append(fl_suites)
            return self.second_level_suites
        for suite in sl_suites.values():
            cur_suite={}
            cur_suite['name']=suite['name']
            cur_suite['id']=suite['id']
            self.second_level_suites.append(cur_suite)
        return self.second_level_suites

    def get_testcases_for_suite(self, suite_id):
        self.current_case = []
        suite_case = self.tlc.getTestCasesForTestSuite(suite_id, 'deep','details')
        for case in suite_case:
            cur_case = {}
            cur_case['id'] = case['id']
            cur_case['external_id'] = case['external_id']
            self.current_case.append(cur_case)
        return self.current_case

    def creat_case_for_suite(self,case_info):
        """ createTestCase: Create a test case
                positional args: testcasename, testsuiteid, testprojectid, authorlogin,
                                 summary
                optional args : steps, preconditions, importance, executiontype, order,
                                internalid, checkduplicatedname, actiononduplicatedname,
                                status, estimatedexecduration
                steps:
                [{'step_number' : 1, 'actions' : "action A" ,
                'expected_results' : "result A", 'execution_type' : 0},
                 {'step_number' : 2, 'actions' : "action B" ,
                'expected_results' : "result B", 'execution_type' : 1},
                 {'step_number' : 3, 'actions' : "action C" ,
                'expected_results' : "result C", 'execution_type' : 0}]
        """
        testcasename = case_info['name']
        print(testcasename)
        testsuiteid = case_info['testsuite_id']
        testprojectid = case_info['testprojectid']
        authorlogin = case_info['author_login']
        summary = case_info['summary']
        preconditions = case_info['preconditions']
        # importance = case_info['importance']
        executiontype = case_info['execution_type']
        # order = case_info['node_order']
        # internalid = case_info['tc_external_id']
        status = case_info['status']
        steps = []
        for step in case_info['steps']:
            s={}
            s['step_number']=step['step_number']
            s['actions']=step['actions']
            s['expected_results']=step['expected_results']
            s['execution_type']=step['execution_type']
            steps.append(s)
        self.tlc.createTestCase(testcasename,testsuiteid,testprojectid,authorlogin,summary,preconditions=preconditions,executiontype=executiontype,status=status,steps=steps)

def get_case_info_excel(wb,testlink):
    suites=wb.sheetnames
    case_in_suite = []
    for suite in suites:
        if suite != ui.lineEdit_2.text():
            continue
        ws=wb[suite]
        row=2   #行，由于第一行为标题行，跳过
        column=0 #列，列计数从0开始
        case_info={}
        step = {}
        suite_id = testlink.current_suite_info['id']
        while ws[row][8].value:
            # print(ws[row][8].value,type(ws[row][8].value))
            if ws[row][8].value == '1':
                case_info={}
                case_info['testprojectid']=testlink.current_project
                case_info['suite_name1']= ws[row][0].value
                case_info['suite_name2'] = ws[row][1].value
                if case_info['suite_name2'] is not None and case_info['suite_name2'] != testlink.current_suite_info['name']:
                    find = False
                    for suite in testlink.second_level_suites:
                        if suite['name'] == case_info['suite_name2']:
                            suite_id = suite['id']
                            find = True
                            break
                    if not find:
                        print("suite--'{}'--未找到！！".format(case_info['suite_name2']))
                        sys.exit(0)
                case_info['testsuite_id'] = suite_id  #写入到一级目录下，建议新建目录并以此目录进行新建用例
                case_info['name']= ws[row][3].value
                case_info['summary']= ws[row][4].value if ws[row][4].value else ''
                case_info['author_login']= ws[row][12].value if ws[row][12].value else 'zhangshuwen'
                case_info['preconditions']= ws[row][5].value if ws[row][5].value else ''
                # case_info['status']= ws[row][6].value if ws[row][6].value else 'active'
                case_info['status']= str(ws[row][13].value)
                case_info['execution_type']= '2' if ws[row][7].value == 'auto' else '1'
                case_info['steps'] = []
                while ws[row][8].value:
                    step['step_number']=str(ws[row][8].value)
                    step['actions']=ws[row][9].value if ws[row][9].value else ''
                    step['expected_results']=ws[row][10].value if ws[row][10].value else ''
                    step['execution_type']='2' if ws[row][11].value == 'auto' else '1'
                    case_info['steps'].append(step)
                    # print('sss')
                    step = {}
                    row+=1
                    if ws[row][8].value =='1':
                        break
                case_in_suite.append(case_info)
    return case_in_suite

if __name__ == '__main__':
    app = QApplication(sys.argv)
    MainWindow = QMainWindow()
    ui = Ui_MainWindow()

    ui.setupUi(MainWindow)
    ui.get_textcase()

    MainWindow.show()
    sys.exit(app.exec_())

